package phototagger.imgsim;

import java.awt.Image;
import java.io.File;
import java.io.IOException;
import java.util.*;

public class WaveletSimilarityFinder extends SimilarityFinder {

    public WaveletSimilarityFinder(File reference) throws IOException {
    	super(reference);
	}

	public static double imageDistance ( Image i1, Image i2 ) {
		double aux1 = imageDistanceAux(i1,i1);
		double aux2 = imageDistanceAux(i2,i2);
		double aux3 = imageDistanceAux(i1,i2);
		double aux4 = Math.max(aux1,aux2);
		return aux4 - aux3;
	}

	public static double imageDistanceAux ( Image i1, Image i2 ) {
		Double score = null;
		try {
			YIQImage img1 = new YIQImage(NaiveSimilarityFinder.toBufferedImage(i1));
			YIQImage img2 = new YIQImage(NaiveSimilarityFinder.toBufferedImage(i2));
			YIQSignature sig1 = img1.getSignature();
			YIQSignature sig2 = img2.getSignature();
			Iterator<String> colors = sig1.getColors();
			while(colors.hasNext()) {
				String color = colors.next();
				score = doScore(sig1.getCoeffients(color), sig1.getAverage(color), color, sig1.getWeights(color), score, sig2);
			}
    	} catch ( Exception ex ) { }
    	return score;
	}
	
    private static double doScore(int[][] coeffients, double channelAvg, String color, double[] weights, Double myScore, YIQSignature sig) throws Exception {
    	if (weights.length != 6) {
    		String m = "Weight array wrong length (" + weights.length + ")";
    		throw new IllegalArgumentException(m);
    	}
    	myScore = setInitialScore(color, weights[0], channelAvg, myScore, sig);
    	for(int y=0; y < coeffients.length; y++) {
    		for(int x=0; x < coeffients[0].length; x++) {
    			if (coeffients[y][x] == 0) continue;
    			myScore -= weights[bin(x,y)];
    		}
    	}
    	return myScore;
    }

    private static double setInitialScore(String color, double weight, double channelAvg, Double currentScore, YIQSignature currentImage) throws Exception {
    	double targetAvg = currentImage.getAverage(color);
    	if(currentScore == null) currentScore = 0.0;
	    return weight * Math.abs(targetAvg - channelAvg) + currentScore;
    }
        
    private static int bin(int x, int y) {
    	double ln_2 = Math.log(2);
    	double bin = Math.min(Math.max(Math.floor(Math.log(x)/ln_2), Math.floor(Math.log(y)/ln_2)), 5d);
    	Double d = new Double(bin);
    	return d.intValue();
    }

}